"""

.. module:: httptask
.. moduleauthor:: @author: 

The purpose of this module is ... 
   
.. seealso:: modules :mod:`<module>`

*Constants*:
.. data:: constant_1
        
    constant_1 is used for ......
        
.. data:: constant_2
        
    constant_2 is used for ......
"""

# Imports
#
import logging

from runtime.task import Task
from datastore.resource import Resource
from httpserver import HTTPThread

# Constants
#

# Classes
#

class HTTPTask(Task):
    """This class is a daemon task. It uses a separate thread to generate messages.
    In this version, a single multi-threaded HTTPTask will support all incoming 
    HTTP requests.
    
    self.spaces contains a reference to the SpacesQueue for this task.
    RequestHandler does the real work.
    
    configuration.xml contains the definitions and documentation.

    *Usage examples*::

        example code

    *Common Arguments for Class methods*:
        <arguments>
    
    *Audience*:
        <audience>
    
    *Constants*:
        .. data:: constant_1
        
            constant_1 is used for ......
        
        .. data:: constant_2
        
            constant_2 is used for ......
    """
    
    # Class constants
    #
    
    def __init__(self, ds, spaces, task_uri, task_def_rs):
        # <purpose>
        #
        # Parameters:
        #     ds : datastore.database.DataStore
        #     spaces : runtime.spaces.Spaces - Reference to spaces
        #     task_uri : String - the uri for this task. The root uri will most
        #                likely be the 'binding' uri for this task.
        #     task_def_rs : datastore.resource.Resource - Task definition resource.
        #
        
        Task.__init__(self, ds, spaces, task_uri, task_def_rs)
        
        self.logger = logging.getLogger('yarra.services.httptask.HTTPTask')
        self.logger.log(logging.DEBUG, "ds (%s), spaces (%s), task_uri (%s), task_def_rs (%s)" % (ds, spaces, task_uri, task_def_rs.pretty_print()))
        
        # dictionary, indexed by site port number, including:
        # - HTTPThread
        # - list of site titles
        #
        self.server_threads = {}
        
    def cb_start(self, method, uri, new_uri, result, context, **kwargs):
        """This function is called when the task starts. 
        
        :param method: <class> or <type>
        :param uri: The task Uri.
        :param new_uri: <class> or <type>
        :param result: <class> or <type>
        :param context: <class> or <type>
        :param kwargs: <class> or <type>
        """
        
        self.logger.log(logging.DEBUG, "***************** uri (%s), new_uri (%s)" % (uri, new_uri))
        
        # Make sure we look for ALL Uris that are children of our task Uri.
        # (i.e. make a collection of our Uri. 
        #
        # All 'put' messages to this task are interpreted as site additions.
        # All 'delete' messages to this task are interpreted as site deletions.
        #
        self.spaces_context.watch_all(['put'], uri.collection(True), 'cb_add_site')
        self.spaces_context.watch_all(['delete'], uri.collection(True), 'cb_delete_site')
        
    def cb_stop(self, method, uri, res, result, context, **kwargs):
        """This function will stop any listeners still running.
        
        :param method: <class> or <type>
        :param uri: The task Uri.
        :param res: <class> or <type>
        :param result: <class> or <type>
        :param context: <class> or <type>
        :param kwargs: <class> or <type>
        """
        
        self.logger.log(logging.DEBUG, "method (%s), uri (%s)" % (method, uri))
        
        # TODO: shutdown() all HTTPThreads.
        #
    
    def cb_add_site(self, method, uri, new_uri, result, context, **kwargs):
        """This function adds a new HTTP server site - watch_all on uri.listener
        
        res.listen = uri to listen in.
        
        :param method: <class> or <type>
        :param uri: The task Uri.
        :param new_uri: <class> or <type>
        :param result: <class> or <type>
        :param context: <class> or <type>
        :param kwargs: <class> or <type>
        """
        
        self.logger.log(logging.DEBUG, "uri (%s), new_uri (%s), result (%s)" % (uri, new_uri, result))
        
        rs = self.spaces_context.get(uri)
        if (rs is not None):
            self.logger.log(logging.DEBUG, "****************** resource (%s)" % rs.pretty_print())
            
            title = rs.get_single_metadata(Resource.KEY_URI_DUBLINCORE_TITLE)
            description = rs.get_single_metadata(Resource.KEY_URI_DUBLINCORE_DESCRIPTION)
            ip_address = rs.get_single_metadata(Resource.KEY_URI_SERVICES_TYPE_HTTP_SITE_IPADDRESS)
            port = rs.get_single_metadata(Resource.KEY_URI_SERVICES_TYPE_HTTP_SITE_PORT)
            default = rs.get_single_metadata(Resource.KEY_URI_SERVICES_TYPE_HTTP_SITE_DEFAULT)
            templateuri = rs.get_single_metadata(Resource.KEY_URI_SERVICES_TYPE_HTTP_SITE_TEMPLATEURI)
            action_list = rs.get_single_metadata(Resource.KEY_URI_SERVICES_TYPE_HTTP_SITE_ACTIONS)
            transforms_dict = rs.get_single_metadata(Resource.KEY_URI_SERVICES_TYPE_HTTP_SITE_TRANSFORMS)
            
            self.logger.log(logging.DEBUG, "title (%s), description (%s), ip_address (%s), port (%s), default (%s), templateuri (%s), action_list (%s), transforms_dict (%s)" % (title, description, ip_address, port, default, templateuri, action_list, transforms_dict))
            
            if (self.server_threads.has_key(str(port)) is not True):
                self.logger.log(logging.DEBUG, "creating new HTTPThread for port (%s). Adding site (%s)" % (port, title))
                
                http_thread = HTTPThread(self.spaces, ip_address, port)
                http_thread.add_site(title, hostname="", default=default, templateuri=templateuri, action_list=action_list, transforms_dict=transforms_dict)
                http_thread.daemon = True
                http_thread.start()
                
                dict = {}
                dict[str(Resource.KEY_URI_SERVICES_TYPE_HTTP_SITE_THREAD)] = http_thread
                title_list = []
                title_list.append(title)
                dict[str(Resource.KEY_URI_DUBLINCORE_TITLE)] = title_list

                self.server_threads[str(port)] = dict
            else:
                self.logger.log(logging.DEBUG, "adding site (%s) to HTTPThread for port (%s)" % (title, port))
                
                dict = self.server_threads[str(port)]
                title_list = dict[str(Resource.KEY_URI_DUBLINCORE_TITLE)]
                if (title not in title_list):
                    title_list.append(title)
                
                http_thread = dict[str(Resource.KEY_URI_SERVICES_TYPE_HTTP_SITE_THREAD)]
                http_thread.add_site(title, hostname="", default=default, templateuri=templateuri, action_list=action_list, transforms_dict=transforms_dict)
            
            self.logger.log(logging.DEBUG, "server_threads (%s)" % self.server_threads)
            
        return (True, uri)

    def cb_delete_site(self, method, uri, res, result, context, **kwargs):
        """This function deletes a site currently being listened on.
        
        :param method: <class> or <type>
        :param uri: The task Uri.
        :param new_uri: <class> or <type>
        :param result: <class> or <type>
        :param context: <class> or <type>
        :param kwargs: <class> or <type>
        """
        
        self.logger.log(logging.DEBUG, "method (%s), uri (%s)" % (method, uri))
        
        rs = self.spaces_context.get(uri)
        if (rs is not None):
            self.logger.log(logging.DEBUG, "****************** resource (%s)" % rs.pretty_print())
            
            title = rs.get_single_metadata(Resource.KEY_URI_DUBLINCORE_TITLE)
            port = rs.get_single_metadata(Resource.KEY_URI_SERVICES_TYPE_HTTP_SITE_PORT)
            
            self.logger.log(logging.DEBUG, "Deleting site (%s), port (%s)" % (title, port))
            
            if (self.server_threads.has_key(str(port)) is True):
                dict = self.server_threads[str(port)]
                
                title_dict = dict[str(Resource.KEY_URI_DUBLINCORE_TITLE)]
                if (title in title_dict):
                    del title_dict[title]
                    http_thread = dict[str(Resource.KEY_URI_SERVICES_TYPE_HTTP_SITE_THREAD)]
                    http_thread.delete_site(title)
                    
                    if (len(title_dict) == 0):
                        # No more sites for this port so shutdown() the port.
                        #
                        self.logger.log(logging.ERROR, "No more sites for port (%s). Shutting-down HTTP server for port." % port)
                        
                        http_thread.shutdown()
                        del self.server_threads[str(port)]
                
#                title_list = dict[str(Resource.KEY_URI_DUBLINCORE_TITLE)]
#                if (title in title_list):
#                    title_list.remove(title)
#                    http_thread = dict[str(Resource.KEY_URI_SERVICES_TYPE_HTTP_SITE_THREAD)]
#                    http_thread.delete_site(title)
#                    
#                    if (title_list.count() == 0):
#                        # No more sites for this port so shutdown() the port.
#                        #
#                        self.logger.log(logging.ERROR, "No more sites for port (%s). Shutting-down HTTP server for port." % port)
#                        
#                        http_thread.shutdown()
#                        del self.server_threads[str(port)]
                else:
                    self.logger.log(logging.ERROR, "Site (%s) is not known. Cannot delete." % title)
            else:
                self.logger.log(logging.ERROR, "Port (%s) is not known. Cannot delete site (%s)" % (port, title))
        
#        self.http_thread.shutdown()
#        self.http_thread.socket.close()
        return (True, uri)